<?php

use GuzzleHttp\Client;
use Illuminate\Database\Capsule\Manager as DB;

/**
 * document https://www.joinpay.com/open-platform/pages/document.html
 */
class JoinPay
{
    protected $merchantNo = '888114700002772'; // 商户编号
    protected $tradeMerchantNo = '777143400373248'; // 报备商户号
    protected $md5Key = '4780767e25564993ab1e3bc9e6c479c4'; // 商户密钥
    protected $payUrl = 'https://www.joinpay.com/trade/uniPayApi.action';
    protected $notifyUrl = '';
    protected $refundUrl = 'https://www.joinpay.com/trade/refund.action';

    private $frpCode = '';

    public function __construct($frpCode, $config = [])
    {
        $this->notifyUrl = Yaf_Registry::get('config')->application->hostUrl . '/pay/joinPayNotify';
        foreach ($config as $k => $v) {
            if (in_array($k, ['merchantNo', 'tradeMerchantNo', 'md5Key', 'payUrl', 'notifyUrl', 'refundUrl']) && !empty($v)) {
                $this->$k = $v;
            }
        }
        $this->frpCode = $frpCode;
        $this->notifyUrl .= "/type/{$frpCode}";
    }

    public function pay($order)
    {
        $openid = $this->checkOpenid($order->member_id);
        if ($openid === false) return 'no openid';

        $payData = [
            'p0_Version'         => '1.0',
            'p1_MerchantNo'      => $this->merchantNo,
            'p2_OrderNo'         => $order->order_sn,
            'p3_Amount'          => $order->total_fee,
            'p4_Cur'             => 1,
            'p5_ProductName'     => $order->order_sn,
            'p9_NotifyUrl'       => $this->notifyUrl,
            'q1_FrpCode'         => $this->frpCode,
            'q5_OpenId'          => $openid,
            'q7_AppId'           => 'wx0d397f0720ebf3af',
            'qa_TradeMerchantNo' => $this->tradeMerchantNo,
        ];
        $payData['hmac'] = $this->signature($payData);
        Log::out('joinPay-payData:' . json_encode($payData), 'joinpay');
        $client = new Client();
        $response = $client->post($this->payUrl, ['form_params' => $payData]);
        Log::out('joinPay-pay response:' . $response->getBody(), 'joinpay');
        $data = json_decode($response->getBody());
        if ($data->ra_Code !== 100) return $data->rb_CodeMsg;
        return $data->rc_Result;
    }

    public function notify($data): string
    {
        Log::out('joinPayNotify-data:' . json_encode($data), 'joinpay');
        $data = $this->signature($data);
        if (!$data) return 'error';
        try {
            $data = (object)$data;
            $order = orderModel::where('order_sn', $data->r2_OrderNo)->first();
            if (!$order || $order->status > 0) return 'success';
            if (intval($data->r6_Status) !== 100) return 'error';

            DB::transaction(function () use ($order, $data) {
                if (strstr($this->frpCode, 'WEIXIN')) $type = 'wx';
                elseif (strstr($this->frpCode, 'ALIPAY')) $type = 'ali';
                $notify = [
                    'order_sn'       => $data->r2_OrderNo,
                    'transaction_id' => $data->r7_TrxNo,
                    'notify_time'    => $data->ra_PayTime,
                    'buyer_email'    => $order->phone,
                    'payment_json'   => json_encode($data),
                    'pay_fee'        => $data->r3_Amount,
                    'pay_type'       => "JoinPay-{$type}"
                ];
                $order->paymentProcess($order, $notify);
            });
            return 'success';
        } catch (\Exception $e) {
            Log::out('joinPayNotify-error:' . $e->getMessage(), 'joinpay');
            return 'error';
        }
    }

    public function refund($order, $refund)
    {
        $reqData = [
            'p1_MerchantNo'    => $this->merchantNo,
            'p2_OrderNo'       => $order->order_sn,
            'p3_RefundOrderNo' => $refund->refund_sn,
            'p4_RefundAmount'  => $refund->refund_fee,
            'p6_NotifyUrl'     => $this->notifyUrl,
        ];
        $reqData['hmac'] = $this->signature($reqData);
        $response = (new Client())->post($this->refundUrl, ['form_params' => $reqData]);
        $data = json_decode($response->getBody());
        if ($data->ra_Status === 100) return true;
        return $data->rc_CodeMsg;
    }


    private function checkOpenid($memberId)
    {
        if (!in_array($this->frpCode, ['WEIXIN_GZH', 'WEIXIN_XCX'])) return '';
        $oauth = memberoauthModel::where([
            'member_id'  => $memberId,
            'provider' => 'Wechat',
            'platform' => 'wxOfficialAccount'
        ])->first();
        if ($oauth) return $oauth->openid;
        return false;
    }

    private function signature($data)
    {
        $tmp = [];
        $preStr = '';
        foreach ($data as $k => $v) {
            $v = urldecode($v);
            $tmp[$k] = $v;
            if ($k !== 'hmac' && $v) $preStr .= $v;
        }
        $str = $preStr . $this->md5Key;
        $hmac = md5($str);
        if (isset($data['hmac'])) return $hmac === $data['hmac'] ? $tmp : false;
        return $hmac;
    }
}